package net.pws.common.beanutils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;

@SuppressWarnings("rawtypes")
public class BeanUtils extends org.apache.commons.beanutils.BeanUtils {
    
    protected static final Log logger = LogFactory.getLog(BeanUtils.class);
    
    static {
        ConvertUtils.initRegister();
    }
    
    public static void copyProperties(Object dest, Object orig) {
        try {
            org.apache.commons.beanutils.BeanUtils.copyProperties(dest, orig);
        }
        catch (IllegalAccessException e) {
        }
        catch (InvocationTargetException e) {
        }
    }
    
    public static void copyProperties(Object dest, Object orig, String[] ignoreProperties){
    	org.springframework.beans.BeanUtils.copyProperties(orig, dest, ignoreProperties);
    }
    
    /**
     * @param bean
     * @param propertyName
     * @return
     */
    public static Method getSetterMethod(Object bean, String propertyName) {
        return getSetterMethod(bean.getClass(), propertyName);
    }
    
    /**
     * @param clazz
     * @param propertyName
     * @return
     */
	public static Method getSetterMethod(Class clazz, String propertyName) {
        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(clazz,
                                                                      propertyName);
        return (propertyDescriptor != null) ? propertyDescriptor.getWriteMethod()
                                           : null;
    }
    
    /**
     * @param bean
     * @param propertyName
     * @return
     */
    public static Method getGetterMethod(Object bean, String propertyName) {
        return getGetterMethod(bean.getClass(), propertyName);
    }
    
    /**
     * @param clazz
     * @param propertyName
     * @return
     */
    public static Method getGetterMethod(Class clazz, String propertyName) {
        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(clazz,
                                                                      propertyName);
        return (propertyDescriptor != null) ? propertyDescriptor.getReadMethod()
                                           : null;
    }
    
    /**
     * @param bean
     * @param propertyName
     * @return
     */
    public static Class getPropertyType(Object bean, String propertyName) {
        return getPropertyType(bean.getClass(), propertyName);
    }
    
    /**
     * @param clazz
     * @param propertyName
     * @return
     */
    public static Class getPropertyType(Class clazz, String propertyName) {
        PropertyDescriptor propertyDescriptor = getPropertyDescriptor(clazz,
                                                                      propertyName);
        return (propertyDescriptor != null) ? propertyDescriptor.getPropertyType()
                                           : null;
    }
    
    /**
     * @param bean
     * @param propertyName
     * @return
     */
    public static PropertyDescriptor getPropertyDescriptor(Object bean,
                                                           String propertyName) {
        return getPropertyDescriptor(bean.getClass(), propertyName);
    }
    
    /**
     * @param clazz
     * @param propertyName
     * @return
     */
    public static PropertyDescriptor getPropertyDescriptor(Class clazz,
                                                           String propertyName) {
        PropertyDescriptor[] descriptors = BeanUtilsBean.getInstance()
                                                        .getPropertyUtils()
                                                        .getPropertyDescriptors(clazz);
        if (descriptors != null) {
            for (PropertyDescriptor descriptor : descriptors) {
                if (propertyName.equals(descriptor.getName()))
                    return descriptor;
            }
        }
        return null;
    }
    
    /**
     * @param clazz
     * @param propertyName
     * @return
     */
    public static PropertyDescriptor[] getPropertyDescriptors(Object bean) {
        return BeanUtilsBean.getInstance()
                            .getPropertyUtils()
                            .getPropertyDescriptors(bean.getClass());
    }
    
    /**
     * @param clazz
     * @param propertyName
     * @return
     */
    public static PropertyDescriptor[] getPropertyDescriptors(Class clazz) {
        return BeanUtilsBean.getInstance()
                            .getPropertyUtils()
                            .getPropertyDescriptors(clazz);
    }

    /**
     * 
     */
    static public Object getDeclaredProperty(Object object, String propertyName) throws IllegalAccessException,
                                                                                NoSuchFieldException {
        Assert.notNull(object);
        Assert.hasText(propertyName);
        Field field = object.getClass().getDeclaredField(propertyName);
        return getDeclaredProperty(object, field);
    }
    
    @SuppressWarnings("unchecked")
	static public Map<String, Object> convertJavaBeanToMap(Object object) throws InvocationTargetException,
                                                                         NoSuchMethodException,
                                                                         IllegalAccessException {
        Map<String, Object> r_map = new HashMap<String, Object>();
        r_map = PropertyUtils.describe(object);
        return r_map;
    }
    
    /**
     * 
     */
    static public Object getDeclaredProperty(Object object, Field field) throws IllegalAccessException {
        Assert.notNull(object);
        Assert.notNull(field);
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        Object result = field.get(object);
        field.setAccessible(accessible);
        return result;
    }
    
    /**
     * 
     */
    static public void setDeclaredProperty(Object object,
                                           String propertyName,
                                           Object newValue) throws IllegalAccessException,
                                                           NoSuchFieldException {
        Assert.notNull(object);
        Assert.hasText(propertyName);
        
        Field field = object.getClass().getDeclaredField(propertyName);
        setDeclaredProperty(object, field, newValue);
    }
    
    /**
     * 
     */
    static public void setDeclaredProperty(Object object,
                                           Field field,
                                           Object newValue) throws IllegalAccessException {
        boolean accessible = field.isAccessible();
        field.setAccessible(true);
        field.set(object, newValue);
        field.setAccessible(accessible);
    }
    
    /**
     * 
     */
    static public Object invokePrivateMethod(Object object,
                                             String methodName,
                                             Object[] params) throws NoSuchMethodException,
                                                             IllegalAccessException,
                                                             InvocationTargetException {
        Assert.notNull(object);
        Assert.hasText(methodName);
        Class[] types = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            types[i] = params[i].getClass();
        }
        Method method = object.getClass().getDeclaredMethod(methodName, types);
        
        boolean accessible = method.isAccessible();
        method.setAccessible(true);
        Object result = method.invoke(object, params);
        method.setAccessible(accessible);
        return result;
    }
    
}
